﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FotoAnotSharp.Anotator
{
    public partial class AnotationPanel : UserControl
    {
        private int mOffsetX, mOffsetY;
        private float mScale = 1.0f;

        public static PointF CountPoint(PointF p, int offsetX, int offsetY, float scale)
        {
            return new PointF(p.X * scale + offsetX, p.Y * scale + offsetY);
        }
        public static PointF CountPointBack(PointF p, int offsetX, int offsetY, float scale)
        {
            return new PointF((p.X - offsetX) / scale, (p.Y - offsetY) / scale);
        }

        private AnotationEditor mEditor;
        public AnotationEditor Editor { get { return mEditor; } set { mEditor = value; } }

        private Cursor mDefault;

        public AnotationPanel()
        {
            InitializeComponent();
            DoubleBuffered = true;
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            mDefault = Cursor;
        }

        private Point mLastMousePos = new Point();
        public Point LastCursor { get { return mLastMousePos; } }

        private int mSelectedIndex = 0;
        private bool mSelectedMoved = false;
        private AnotPolygonPoint mSelectedPoint;
        private AnotPolygon mSelectedPolygon;
        public AnotPolygon SelectedPolygon
        {
            get { return mSelectedPolygon; }
            set
            {
                if (mSelectedPolygon == value)
                    return;

                if (mSelectedPolygon != null)
                    mSelectedPolygon.Selected = false;
                mSelectedPolygon = value;
                if (mSelectedPolygon != null)
                {
                    mSelectedPolygon.Selected = true;
                    mSelectedPolygon.LastSelected = ++mSelectedIndex;
                }

                Editor.OnPolygonSelected(mSelectedPolygon);

                Invalidate();
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            if (mNewPolygon != null)
                return;

            PointF point = CountPointBack(e.Location, mOffsetX, mOffsetY, mScale);

            mSelectedMoved = false;

            List<AnotPolygon> toSel = new List<AnotPolygon>(5);
            AnotPolygonPoint refered;
            foreach (AnotPolygon p in Editor.Polygons.OrderByDescending(t => t.LastSelected))
            {
                if (p.Contains(point, AnotPolygonPoint.DRAW_SIZE / mScale, out refered))
                { toSel.Add(p); p.LastSelectedPoint = refered; }
            }
            AnotPolygon newSelected = toSel.OrderByDescending(t => t.LastSelected).FirstOrDefault();
            
            SelectedPolygon = newSelected;

            if (newSelected != null)
            {
                foreach (AnotPolygonPoint p in newSelected.Points)
                {
                    if (p.GetDistance(point) <= AnotPolygonPoint.DRAW_SIZE / mScale)
                    {
                        mSelectedPoint = p;
                        Cursor = Cursors.SizeAll;
                        break;
                    }
                }
            }
        }


        protected override void OnMouseUp(MouseEventArgs e)
        {
            if (mSelectedPoint != null && mSelectedMoved)
            {
                mSelectedPoint.RaiseMoved();
                mEditor.RaisePolygonMoved(mSelectedPolygon);
            }

            mSelectedPoint = null;
            Cursor = mDefault;
            Invalidate();
        }

        protected override void OnMouseClick(MouseEventArgs e)
        {
            if ((e.Button & MouseButtons.Right) == MouseButtons.Right)
            {
                if (mNewPolygon != null)
                {
                    EndAddPolygon();
                    return;
                }

                if (mSelectedPolygon == null)
                    return;

                contextMenuStripPolygon.Show(this, e.Location);
            }
            else if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                if (mNewPolygon != null)
                {
                    mNewPolygon.Add(CountPointBack(e.Location, mOffsetX, mOffsetY, mScale));
                    Invalidate();
                }
            }
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            int diff_x = (e.Location.X - mLastMousePos.X);
            int diff_y = (e.Location.Y - mLastMousePos.Y);

            if ((e.Button & MouseButtons.Left) == MouseButtons.Left)
            {
                if (mSelectedPoint == null)
                { mOffsetX += diff_x; mOffsetY += diff_y; }
                else
                {
                    mSelectedMoved = true;
                    if ((Control.ModifierKeys & Keys.Control) == Keys.Control)
                    {
                        foreach (AnotPolygonPoint po in mSelectedPolygon.Points)
                            po.Move(diff_x / mScale, diff_y / mScale);
                    }
                    else
                        mSelectedPoint.Move(diff_x / mScale, diff_y / mScale);
                }

                Invalidate();
            }

            if (mNewPolygon != null)
                Invalidate();

            mLastMousePos = e.Location;
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);

            if (e.Delta > 0)
                ZoomScale(1.2f, e.X, e.Y);
            else if (e.Delta < 0)
                ZoomScale(0.8f, e.X, e.Y);

        }

        protected override void OnPaintBackground(PaintEventArgs e)
        {
            e.Graphics.SetClip(new RectangleF(0, 0, Width, Height));
            e.Graphics.Clear(BackColor);

            if (DesignMode)
                return;

            if (Editor.Background != null)
                e.Graphics.DrawImage(Editor.Background, mOffsetX, mOffsetY, Editor.Background.Width * mScale, Editor.Background.Height * mScale);
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            e.Graphics.SetClip(new RectangleF(0, 0, Width, Height));
            e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            if (DesignMode)
                return;

            foreach (AnotPolygon line in Editor.Polygons)
            {
                line.Draw(e.Graphics, mOffsetX, mOffsetY, mScale);
            }

            if (mNewPolygon != null)
            {
                DrawNewPolygon(e.Graphics);
            }
        }
        public void ZoomScale(float scale)
        {
            ZoomScale(scale, Width / 2, Height / 2);
        }

        public void ZoomScale(float scale, int x, int y)
        {
            this.mScale *= scale;
            this.mScale = Math.Max(0.01f, mScale);

            this.mOffsetX = (int)Math.Round((this.mOffsetX - x) * scale + x);
            this.mOffsetY = (int)Math.Round((this.mOffsetY - (y)) * scale + (y));

            Invalidate();
        }

        public void Autofit()
        {
            this.mOffsetX = 0;
            this.mOffsetY = 0;

            if (Editor.Background == null)
                mScale = 1.0f;
            else
                mScale = Math.Min((float)Width / Editor.Background.Width, (float)Height / Editor.Background.Height);

            Invalidate();
        }

        private List<PointF> mNewPolygon = null;
        public void AddPolygon()
        {
            SelectedPolygon = null;
            mNewPolygon = new List<PointF>();

        }
        public void EndAddPolygon()
        {
            if (mNewPolygon.Count > 2)
            {
                AnotPolygon p = mEditor.AddPolygon(mNewPolygon.ToArray());
                SelectedPolygon = p;
            }
            mNewPolygon = null;

            Invalidate();
        }

        const int C_NEW_POLYGON_CURSOR = 15;
        void DrawNewPolygon(Graphics g)
        {
            if (mNewPolygon.Count == 0)
            {
                g.DrawEllipse(AnotPolygon.Pen, new RectangleF(mLastMousePos.X - C_NEW_POLYGON_CURSOR, mLastMousePos.Y - C_NEW_POLYGON_CURSOR, C_NEW_POLYGON_CURSOR * 2, C_NEW_POLYGON_CURSOR * 2));
            }
            else
            {
                PointF[] ps = mNewPolygon.Select(t => CountPoint(t, mOffsetX, mOffsetY, mScale)).Concat(new PointF[] { mLastMousePos }).ToArray();
                g.DrawPolygon(AnotPolygon.Pen, ps);
            }
        }

        private void přidatBodToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mSelectedPolygon == null)
                return;
            mSelectedPolygon.AddPoint(CountPointBack(mLastMousePos, mOffsetX, mOffsetY, mScale), mSelectedPolygon.LastSelectedPoint);
            mSelectedPolygon.LastSelectedPoint = null;
            Invalidate();
        }

        private void odebratBodToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mSelectedPolygon == null)
                return;
            mSelectedPolygon.RemovePoint(mSelectedPolygon.LastSelectedPoint);
            mSelectedPolygon.LastSelectedPoint = null;
            Invalidate();

        }

        private void smazatToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (mSelectedPolygon == null)
                return;
            mEditor.RemovePolygon(mSelectedPolygon);
            Invalidate();
        }
    }
}
